package com.wyclabs.auth.common;

import com.wyclabs.auth.common.constants.GrantType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter;
import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter;
import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter;
import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;

import java.util.*;

/**
 * SecurityTokenGranter
 * @author sr
 * @date 2019/10/16
 */
@Slf4j
public class SecurityTokenGranter implements TokenGranter {
    private static Set<String> grantTypes = new HashSet<>();
    private final List<TokenGranter> tokenGranters = new ArrayList<>();
    private ClientDetailsService clientDetails;
    private AuthorizationServerTokenServices authorizationServerTokenServices;
    private AuthorizationCodeServices authorizationCodeServices;
    private OAuth2RequestFactory requestFactory;
    private AuthenticationManager authenticationManager;
    private AuthorizationServerEndpointsConfigurer endpoints;
    private boolean init = false;

    private SecurityTokenGranter(AuthorizationServerEndpointsConfigurer endpoints) {
        this.endpoints = endpoints;
    }

    public static String[] grantTypes(String... types) {
        if (types.length == 0) {
            throw new RuntimeException("SecurityTokenGranter未设置grantTypes!");
        }
        grantTypes.addAll(Arrays.asList(types));
        return types;
    }

    public static SecurityTokenGranter getInstance(AuthorizationServerEndpointsConfigurer endpoints) {
        return new SecurityTokenGranter(endpoints);
    }

    public SecurityTokenGranter authenticationManager(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
        return this;
    }

    private void initGranter() {
        if (grantTypes.size() == 0) {
            throw new RuntimeException("XksTokenGranter未设置grantTypes!");
        }
        log.info("初始化spring security granters:{}", grantTypes);
        clientDetails = endpoints.getClientDetailsService();
        authorizationCodeServices = endpoints.getAuthorizationCodeServices();
        authorizationServerTokenServices = endpoints.getDefaultAuthorizationServerTokenServices();
        requestFactory = endpoints.getOAuth2RequestFactory();
        tokenGranters.addAll(tokenGranters());
    }

    @Override
    public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
        for (TokenGranter granter : tokenGranters) {
            OAuth2AccessToken grant = granter.grant(grantType, tokenRequest);
            if (grant != null) {
                return grant;
            }
        }
        if (tokenGranters.size() == 0 && !init) {
            initGranter();
            init = true;
            return grant(grantType, tokenRequest);
        }
        return null;
    }

    private List<TokenGranter> tokenGranters() {
        List<TokenGranter> tokenGranters = new ArrayList<>();
        if (grantTypes.contains(GrantType.AUTHORIZATION_CODE.getName())) {
            tokenGranters.add(new AuthorizationCodeTokenGranter(authorizationServerTokenServices, authorizationCodeServices, clientDetails, requestFactory));
        }
        if (grantTypes.contains(GrantType.IMPLICIT.getName())) {
            ImplicitTokenGranter implicit = new ImplicitTokenGranter(authorizationServerTokenServices, clientDetails, requestFactory);
            tokenGranters.add(implicit);
        }
        if (grantTypes.contains(GrantType.CLIENT_CREDENTIALS.getName())) {
            tokenGranters.add(new ClientCredentialsTokenGranter(authorizationServerTokenServices, clientDetails, requestFactory));
        }
        if (grantTypes.contains(GrantType.REFRESH_TOKEN.getName())) {
            tokenGranters.add(new RefreshTokenGranter(authorizationServerTokenServices, clientDetails, requestFactory));
        }
        if (grantTypes.contains(GrantType.PASSWORD.getName())) {
            if (authenticationManager != null) {
                tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, authorizationServerTokenServices, clientDetails, requestFactory));
            } else {
                log.warn("无法初始化密码验证模式password，原因：SecurityTokenGranter未设置authenticationManager");
            }
        }
        return tokenGranters;
    }
}
